package blackPeter;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import doublelinkedlist.LinkedList;
import doublelinkedlist.exceptions.InvalidDataParameterException;
import doublelinkedlist.exceptions.LinkedListIndexException;

import exceptions.FailedToGetClassNameException;
import exceptions.InvalidArrayForBlackPeterException;

public class BlackPeter {

	private String xClass, yClass;

	public BlackPeter() {

		xClass = yClass = "";

	}

	public void play(Object[] array) throws InvalidArrayForBlackPeterException {

		try {
			validateInput(array);
		} catch (FailedToGetClassNameException e) {
			System.out.println("Something went wrong! Sorry!");
			return;
		}
		Object yInstance = array[0];
		xClass = yClass = array[0].getClass().getName();
		for (int i = 1; i < array.length; i++) {
			String name = array[i].getClass().getName();
			if (name.equals(xClass)) {
				xClass = name;
			}
			if (!yClass.equals(name) && !xClass.equals(name)) {
				yClass = name;
				yInstance = array[i];
			}
		}
		executeY(yInstance);

	}

	private void validateInput(Object[] array)
			throws InvalidArrayForBlackPeterException,
			FailedToGetClassNameException {

		if (array == null || array.length < 3) {
			throw new InvalidArrayForBlackPeterException(
					"Array is null or has number of lements less than 3!");
		}
		LinkedList differentClassesInArray;
		differentClassesInArray = getDifferentClasssesInArray(array);
		if (differentClassesInArray.getSize() != 2) {
			throw new InvalidArrayForBlackPeterException(
					"Array must contain exactly two different classes!");
		}
		checkForClassAppearance(array, differentClassesInArray);

	}

	private void checkForClassAppearance(Object[] array,
			LinkedList twoDifferentClasses)
			throws InvalidArrayForBlackPeterException,
			FailedToGetClassNameException {

		String first = null;
		try {
			first = (String) twoDifferentClasses.get(0);
		} catch (LinkedListIndexException e) {
			e.printStackTrace();
		}
		if (first != null) {
			int firstClassAppearances = 0;
			int secondAppearances = 0;

			for (int i = 0; i < array.length; i++) {
				String name = array[i].getClass().getName();
				if (name.equals(first)) {
					firstClassAppearances++;
				} else {
					secondAppearances++;
				}
			}

			if (firstClassAppearances > 1 && secondAppearances > 1) {
				throw new InvalidArrayForBlackPeterException(
						"Both classes appear more than once!");
			}
		} else {
			throw new FailedToGetClassNameException();
		}

	}

	private LinkedList getDifferentClasssesInArray(Object[] array)
			throws InvalidArrayForBlackPeterException {

		LinkedList ll = new LinkedList();
		for (int i = 0; i < array.length; i++) {
			String name = array[i].getClass().getName();
			if (!ll.contains(name)) {
				try {
					ll.add(name);
				} catch (InvalidDataParameterException e) {
					e.printStackTrace();
				}
			}
		}

		return ll;

	}

	public void executeY(Object obj) {

		Method m = null;
		try {
			m = obj.getClass().getMethod("goAway");
			m.invoke(obj);
		} catch (NoSuchMethodException e) {
			for (Field field : obj.getClass().getDeclaredFields()) {
				field.setAccessible(true);
				String name = field.getName();
				Object value = null;
				try {
					value = field.get(obj);
				} catch (IllegalArgumentException e1) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e2) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.printf("Field name: %s, Field value: %s%n", name,
						value);
			}
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
